package com.situ.util;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSourceFactory;

public class DataSourceUtil {

	// 连接池
	// 数据库连接池，用来管理connection,可以重复的利用
	// 有了连接池就不用自己创建connection了，而是通过池来获取connection.
	// 当使用完之后，把connection归还给池，池就可以再利用这个connection了。

	// 几个常用的基本参数：
	// 初始化大小： 就是池刚创建出来的时候默认带几个连接
	// 最小空闲连接数： 比如：3个
	// 增量: 5个
	// 最大空闲连接数： 20
	// 最大连接数： 50
	// 最大的等待时间： 1000ms

	// 创建连接池 四大参数

	// 1、驱动类
	// 2.连接地址(url)
	// 3.用户名
	// 4.密码

	// 实现 java.sql.DataSource接口

	// 常见的连接池：
	// DBCP C3P0
	// 阿里巴巴 druid

	private static DataSource ds = null;

	static {
		try {
			Properties prop = new Properties();
			prop.load(DataSourceUtil.class.getClassLoader().getResourceAsStream("db.properties"));
			ds = DruidDataSourceFactory.createDataSource(prop);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static Connection getConnection() throws SQLException {
		return ds.getConnection();
	}

	private static void close(PreparedStatement ps, Connection conn) {
		close(null, ps, conn);
	}

	// 连接池里的 关闭连接 close方法
	// 不是真正的把连接关闭了，而是释放连接的使用权限，将这个连接设置成了空闲状态

	private static void close(ResultSet rs, PreparedStatement ps, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	// 所有的DML 用这个方法去执行
	/**
	 * 执行DML数据库操作的统一封装方法
	 * 
	 * @param sql  要执行的SQL语句
	 * @param args 可变长参数，对应SQL语句中的占位符个数和顺序
	 * @return 返回DML语句执行后的影响行数
	 * @throws SQLException
	 */
	public static int executeUpdate(String sql, Object... args) throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = null;
		try {
			ps = conn.prepareStatement(sql);
			for (int i = 1; i <= args.length; i++) {
				ps.setObject(i, args[i - 1]);
			}
			return ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			close(ps, conn);
		}
		return 0;
	}

	public static <T> List<T> executeQuery(String sql, Class<T> cla, Object... args) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		List<T> list = new ArrayList<>();
		Connection conn = getConnection();
		PreparedStatement ps = conn.prepareStatement(sql);
		for (int i = 1; i <= args.length; i++) {// 循环可变长参数替换占位符的值
			ps.setObject(i, args[i - 1]);
		}
		ResultSet rs = ps.executeQuery();
		ResultSetMetaData rsmd = rs.getMetaData();// 获取结果集的 元数据
		while (rs.next()) {
			T o = cla.newInstance();// 动态创建一个实例
			for (int i = 1; i <= rsmd.getColumnCount(); i++) {
				String columnName = rsmd.getColumnLabel(i);// 获取第i列的名字
				Object value = rs.getObject(i);// 获取第i列的值
				try {
					Field field = cla.getDeclaredField(NameUtil.underlineToHump(columnName));// 根据列名获取对象里相应的成员变量
					field.setAccessible(true);// 给私有成员变量设置可以访问的权限
					field.set(o, value);// 成员变量赋值
				} catch (Exception e) {
					e.printStackTrace();
					System.out.println("列[" + columnName + "]处理错误");
				}
			}
			list.add(o);// 把动态创建的实例放到结果list中
		}
		close(rs, ps, conn);
		return list;
	}

	// 统计查询行数
	public static Integer executeCount(String sql, Object... args) throws SQLException {
		Connection conn = getConnection();
		PreparedStatement ps = conn.prepareStatement(sql);
		for (int i = 1; i <= args.length; i++) {// 循环可变长参数替换占位符的值
			ps.setObject(i, args[i - 1]);
		}
		ResultSet rs = ps.executeQuery();
		rs.next();// 结果集移动到第一行
		Integer result = rs.getInt(1);// 获取第一列的值
		close(rs, ps, conn);
		return result;
	}

}
